package com.liuyi.netty.mqtt.processed.process.impl;

import com.liuyi.netty.mqtt.common.MqttConst;
import com.liuyi.netty.mqtt.common.util.MqttMessageUtil;
import com.liuyi.netty.mqtt.configution.MqttBeanManage;
import com.liuyi.netty.mqtt.processed.process.MqttProcess;
import com.liuyi.netty.mqtt.processed.store.MqttChannelManage;
import com.liuyi.netty.mqtt.processed.store.MqttSession;
import com.liuyi.netty.mqtt.processed.store.MqttSessionStore;
import com.liuyi.netty.mqtt.server.AuthServer;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.mqtt.*;
import lombok.extern.slf4j.Slf4j;

/**
 * @author Mr.Fmy
 * @since 2021-08-19
 */
@Slf4j
public class ConnectMqttProcess implements MqttProcess {

    private final MqttBeanManage mqttBeanManage;
    private AuthServer authServer;
    private MqttSessionStore mqttSessionStore;

    public ConnectMqttProcess(MqttBeanManage mqttBeanManage) {
        this.mqttBeanManage = mqttBeanManage;
        this.authServer = mqttBeanManage.getAuthServer();
        this.mqttSessionStore = mqttBeanManage.getMqttSessionStore();
    }

    @Override
    public void process(ChannelHandlerContext channel, MqttMessage msg) {
        MqttConnectMessage message = (MqttConnectMessage) msg;
        MqttConnectReturnCode returnCode;
        int mqttVersion = message.variableHeader().version();
        String clientId = message.payload().clientIdentifier();
        boolean cleanSession = message.variableHeader().isCleanSession();
        String userName = message.payload().userName();
        byte[] pwd = message.payload().passwordInBytes();
        String password = pwd != null ? new String(pwd): "";
        MqttSession clientSession = null;

        boolean sessionPresent = false;

        if (!version(mqttVersion)) {
            //版本验证
            returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_UNSUPPORTED_PROTOCOL_VERSION;
        } else if (!authServer.validClientId(clientId)) {
            returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_CLIENT_IDENTIFIER_NOT_VALID;
        } else if (!authServer.validUser(userName, password)) {
            returnCode = MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD;
        } else {
            returnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED;
        }

        clientSession = mqttSessionStore.getDefaultByClientId(clientId);

        if (clientSession.getState().equals(MqttConst.SessionState.NOT)) {
            //session不存在，创建一个新的session
            createSession(clientId, channel);
        } else {
            if (clientSession.getState().equals(MqttConst.SessionState.ONLINE)) {
                //在线
                MqttChannelManage.close(clientId);
                //创建一个新的session
                createSession(clientId, channel);
            } else {
                //离线
                if (cleanSession) {
                    //丢弃之前的session
                    mqttSessionStore.clearSession(clientId);
                    //重新创建会话
                    createSession(clientId, channel);
                } else {
                    //刷新session
                    mqttSessionStore.refreshSession(clientId);
                    //在本地创建channel对应关系
                    MqttChannelManage.set(clientId, channel);
                    sessionPresent = true;
                }
            }
            returnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED;
        }


        MqttConnAckMessage connAckMessage = MqttMessageUtil.getConnAckMessage(returnCode, sessionPresent);
        channel.writeAndFlush(connAckMessage);
        if (returnCode != MqttConnectReturnCode.CONNECTION_ACCEPTED) {
            channel.close();
            log.warn("[CONNECT] -> {} connect failure,returnCode={}", clientId, returnCode);
            return;
        }

    }

    private void createSession(String clientId, ChannelHandlerContext channel) {
        mqttSessionStore.createSession(clientId);
        MqttChannelManage.set(clientId, channel);
    }

    private boolean version(int version) {
        if (version == 3 || version == 4) {
            return true;
        }
        return false;
    }
}
